<?php
/**
 * I-Cafe framework
 * 
 * @category CF
 * @package CF_Db
 * @link http://www.i-cafe.org
 * @license http://www.i-cafe.org/license
 * @version 04-04-2010
 * @author tcl_java@yahoo.com
 */

/**
 * A class performance for build and execute query INSERT
 * <code>
 * 	$data = array("name" => "Product 1", "price" => 300);
 * 
 * 	$insert = new CF_Db_Insert();
 * 	$insert->table("products")
 * 		->data($data);
 * 	echo $insert; // output: "INSERT INTO products(`name`, `price`) VALUES ('Product 1', 300)"
 * </code>
 */
class CF_Db_Insert extends CF_Component implements CF_Db_Query_Interface
{
	/**
	 * @var string|array|CF_Db_Expr
	 */
	protected $_table;
	
	/**
	 * @var string|array
	 */
	protected $_columns;
	
	/**
	 * @var array
	 */
	protected $_values;
	
	/**
	 * 
	 */
	public function __construct()
	{
		# notify before construct event
		$this->fireEvent('beforeconstruct', array($this));
		
		# register event
		$this->addEvents(
			'beforebuilquery',
			'buildquery',
			'beforeexecute',
			'execute'
		);
		
		$this->reset();
		
		# notify construct event
		$this->fireEvent('construct', array($this));
	}
	
	/**
	 * 
	 */
	public function reset()
	{
		$this->_table = null;
		$this->_columns = null;
		$this->_values = array();	
	}
	
	/**
	 * 
	 * @param string|array|CF_Db_Expr $table
	 * @return CF_Db_Insert this
	 */
	public function table($table)
	{
		$this->_table = $table;
		return $this;
	}
	
	/**
	 * 
	 * @param string|array|CF_Db_Expr $columns
	 * @return CF_Db_Insert this
	 */
	public function columns($columns)
	{
		$this->_columns = $columns;
		return $this;
	}
	
	/**
	 * 
	 * @param string|array|CF_Db_Expr|CF_Db_Select $values
	 * @return CF_Db_Insert this
	 */
	public function values($values)
	{
		if(is_array($values) && is_array(current($values)))
			$this->_values = array_merge($this->_values, $values);
		else
			$this->_values[] = $values;

		return $this;
	}
	
	/**
	 * 
	 * @param string|array|CF_Db_Expr $data
	 * @return CF_Db_Insert this
	 */
	public function data(array $data)
	{
		if(is_array($current = current($data)))
			$this->_columns = array_keys($current);
		
		else
			$this->_columns = array_keys($data);
			
		reset($data);
		
		$this->_values = $data;
	}
	
	/**
	 * Execute INSERT query
	 * @param boolean $returnInsertId
	 * @return boolean
	 */
	public function execute($returnInsertId = true)
	{
		$query = $this->__toString();
		
		# notify before execute event
		if(CF_Observable::VALUE_RETURN === $this->fireEvent('beforeexecute', array($this, &$query, &$returnInsertId))) return;
		
		CF_Db::getInstance()->query($query);
		$res = $returnInsertId?
				CF_Db::getInstance()->getInsertId()
				: CF_Db::getInstance()->getAfftectedRows();
				
		# notify execute event
		$this->fireEvent('execute', array($this, &$res, &$query, &$returnInsertId));
		
		return $res;
	}
	
	/**
	 * Build query INSERT
	 * @return string
	 */
	public function __toString()
	{
		# notify before builquery event
		if(CF_Observable::VALUE_RETURN === $this->fireEvent('beforebuilquery', array($this))) return;
		
		$parts = array();
		$parts[] = 'INSERT ';
		
		# render table
		$parts[] = ($this->_table instanceof CF_Db_Expr)?
					(string) $this->_table
					: CF_Db::getInstance()->quoteIdentifier($this->_table);
		
		# render columns			
		if($this->_columns){
			if($this->_columns instanceof CF_Db_Expr){
				$parts[] = (string) $this->_columns;
			
			}else{
				if(is_string($this->_columns)){
					# strip white space
					$columns = preg_replace('/\s+/', '', $this->_columns);
					$columns = explode(',', $columns);
				}else{
					$columns = $this->_columns;
				}
				
				# quote identifier each columns
				foreach($columns as &$col){
					$col = CF_Db::getInstance()->quoteIdentifier($col);
				}
				
				$parts[] = '(' . implode(',', $columns) . ')';
			}
		}
		
		# render values
		$values = CF_Db::getInstance()->quote($this->_values);
		if(!is_array(current($values))){
			$parts[] = 'VALUES (' . implode(',', $values) . ')';
		}else{
			$val = array();
			foreach ($values as $value) {
				$val[] = implode(',', $value);
			}
			$parts[] = 'VALUES (' . implode('),(', $val) . ')';
		}
		
		$query = implode(' ', $parts);
		
		# notify build query event
		$this->fireEvent('buildquery', array($this, &$query));
		
		return $query;
	}
}
